package com.michaelbulava.TFS.vcs.internal.providers;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.annotate.AnnotationProvider;
import com.intellij.openapi.vcs.annotate.FileAnnotation;
import com.intellij.openapi.vcs.history.VcsFileRevision;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vfs.VirtualFile;
import com.michaelbulava.TFS.Messages;
import com.michaelbulava.TFS.TFSVcsModuleComponent;
import com.michaelbulava.TFS.TFSVcsProjectComponent;
import com.michaelbulava.TFS.vcs.TFSVcs;
import com.michaelbulava.TFS.vcs.internal.TFSFileRevision;
import com.michaelbulava.TFS.vcs.internal.providers.internal.AnnotationBuilder;
import com.michaelbulava.TFS.vcs.internal.providers.internal.TFSFileAnnotation;
import com.michaelbulava.TFS.vcs.utils.TFSFileUtil;
import com.michaelbulava.TFS.vcs.utils.TFSProgressUtil;
import com.microsoft.tfs.core.clients.versioncontrol.GetItemsOptions;
import com.microsoft.tfs.core.clients.versioncontrol.Workstation;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.DeletedState;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.ExtendedItem;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace;
import com.microsoft.tfs.core.clients.versioncontrol.specs.version.ChangesetVersionSpec;
import com.microsoft.tfs.core.clients.versioncontrol.specs.version.VersionSpec;
import com.microsoft.tfs.core.clients.versioncontrol.specs.version.WorkspaceVersionSpec;
import com.microsoft.tfs.core.clients.versioncontrol.workspacecache.WorkspaceInfo;
import org.jetbrains.annotations.Nullable;

import java.text.MessageFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Created by MichaelA on 8/18/2015.
 */
public class TFSAnnotationProvider implements AnnotationProvider {
    private static final int CURRENT_CHANGESET = 0;
    private final TFSVcs myVcs;

    public TFSAnnotationProvider(TFSVcs vcs) {
        myVcs = vcs;
    }

    @Override
    public FileAnnotation annotate(VirtualFile virtualFile) throws VcsException {
        return annotate(virtualFile, CURRENT_CHANGESET);
    }

    @Override
    public FileAnnotation annotate(VirtualFile virtualFile, VcsFileRevision vcsFileRevision) throws VcsException {
        return annotate(virtualFile, ((VcsRevisionNumber.Int)vcsFileRevision.getRevisionNumber()).getValue());
    }

    @Override
    public boolean isAnnotationValid(VcsFileRevision vcsFileRevision) {
        return true;
    }

    @Nullable
    private FileAnnotation annotate(final VirtualFile file, final int changeset) throws VcsException {
        final Ref<VcsException> exception = new Ref<VcsException>();
        final Ref<FileAnnotation>result = new Ref<>();

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try{
                    ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
                    TFSProgressUtil.setIndeterminate(progressIndicator, true);
                    FilePath localPath = TFSFileUtil.getFilePath(file);

                    Workspace workspace = TFSVcs.getWorkspaceForModule(TFSFileUtil.getModuleForFile(file, myVcs.getProject()));

                    TFSProgressUtil.checkCanceled(progressIndicator);
                    if (workspace == null) {
                        exception.set(new VcsException(MessageFormat.format(Messages.getString("TFSAnnotationProvider.FileMappingNotFound"),
                                new Object[]{localPath.getPresentableUrl()})));
                        return;
                    }
                    Map<FilePath, ExtendedItem> path2Item = TFSFileUtil.getFileExtendedItems(myVcs.getProject(),
                            workspace, Collections.singletonList(file), DeletedState.ANY, GetItemsOptions.NONE);
                    if (path2Item.isEmpty()){
                        exception.set(new VcsException(MessageFormat.format("''{0}'' is unversioned", new Object[] { localPath.getPresentableUrl() })));
                        return;
                    }

                    TFSProgressUtil.checkCanceled(progressIndicator);
                    VersionSpec versionSpec = changeset == CURRENT_CHANGESET ?
                            new WorkspaceVersionSpec(workspace) : new ChangesetVersionSpec(changeset);

                    List<TFSFileRevision> revisionList = TFSHistoryProvider.getRevisions(myVcs.getProject(),
                            (path2Item.get(localPath)).getSourceServerItem(), false, workspace, versionSpec);

                    TFSProgressUtil.checkCanceled(progressIndicator);
                    if (revisionList.isEmpty()) {
                        return;
                    }
                    result.set(annotate(workspace, localPath, revisionList));
                } catch(VcsException e){
                    exception.set(e);
                } catch(Throwable e){
                    exception.set(new VcsException(e));
                }
            }
        };
        if(ApplicationManager.getApplication().isDispatchThread())
            ProgressManager.getInstance().runProcessWithProgressSynchronously(runnable, "Computing Annorations", true,
                    myVcs.getProject());
        else
            runnable.run();

        if (!exception.isNull())
            throw exception.get();

        return result.get();
    }

    private FileAnnotation annotate(Workspace workspace, final FilePath localPath,
                                    List<TFSFileRevision> revisions) throws VcsException{
        final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();

        AnnotationBuilder annotationBuilder = new AnnotationBuilder(revisions, new AnnotationBuilder.ContentProvider()
        {
            public String getContent(TFSFileRevision revision)
                    throws VcsException
            {
                TFSProgressUtil.checkCanceled(progressIndicator);

                String content = revision.createContentRevision().getContent();
                if (content == null)
                {
                    String errorMessage = MessageFormat.format("Cannot load content for file ''{0}'', rev. {1}",
                            new Object[] { localPath.getPresentableUrl(),
                                    Integer.valueOf(revision.getRevisionNumber().asString()) });


                    throw new VcsException(errorMessage);
                }
                return content;
            }
        });
        return new TFSFileAnnotation(this.myVcs, workspace, annotationBuilder.getAnnotatedContent(), annotationBuilder.getLineRevisions(), localPath.getVirtualFile());
    }
}
